﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;


namespace ArkanoidX
{
    static class Utility
    {
        public enum HitBorder
        {
            None,
            Left,
            Right,
            Top,
            Bottom
        }

        public static bool Collide(Rectangle rect1, Rectangle rect2)
        {
            if (rect1.Right < rect2.Left)
                return false;
            if (rect1.Left > rect2.Right)
                return false;
            if (rect1.Bottom < rect2.Top)
                return false;
            if (rect1.Top > rect2.Bottom)
                return false;

            return true;
        }

        public static bool Collide(Vector2 center, float radius, Rectangle rect, out Vector2 hitPoint, out HitBorder hitBorder)
        {
            hitBorder = HitBorder.None;
            hitPoint = default(Vector2);

            // this is a simplified and inacurate algorithm that 
            // detects whether these two shapes collide
            // since you may easily see the case when the ball is somewhere 
            // near the corner of the rectangle
            if (center.X + radius < rect.Left)
                return false;
            if (center.X - radius > rect.Right)
                return false;
            if (center.Y + radius < rect.Top)
                return false;
            if (center.Y - radius > rect.Bottom)
                return false;

            // a simplistic algorithm that works out the point of clash
            // which is always on the boundary of the rectangle
            float dx = center.X - rect.Center.X;
            float dy = center.Y - rect.Center.Y;

            float slope = rect.Width / rect.Height;
            float criterion = Math.Abs(dy) * rect.Width - Math.Abs(dx) * rect.Height;
            if (criterion > 0)
            {   // on the horizontal border
                if (dy > 0)
                {   // lower border
                    float px = rect.Height * 0.5f * dx / dy;
                    hitPoint = new Vector2(rect.Center.X + px, rect.Bottom);
                    hitBorder = HitBorder.Bottom;
                }
                else
                {   // upper border
                    float px = -rect.Height * 0.5f * dx / dy;
                    hitPoint = new Vector2(rect.Center.X + px, rect.Top);
                    hitBorder = HitBorder.Top;
                }
            }
            else
            {   // on the vertical border
                if (dx > 0)
                {   // right border
                    float py = rect.Width * 0.5f * dy / dx;
                    hitPoint = new Vector2(rect.Right, rect.Center.Y + py);
                    hitBorder = HitBorder.Right;
                }
                else
                {   // left border
                    float py = -rect.Width * 0.5f * dy / dx;
                    hitPoint = new Vector2(rect.Left, rect.Center.Y + py);
                    hitBorder = HitBorder.Left;
                }
            }

            return true;
        }

        /**
         * position is the central position of the object
         * halfTargetSize is the half size of the sprite mapped on the target screen,
         * i.e. half the size of fullTargetSize
         */
        public static void Draw(SpriteBatch sBatch, Texture2D texture, 
            Vector2 position, Vector2 halfTargetSize, Rectangle sourceRect, Color color)
        {
            Rectangle targetRect = GetRectangle(position, halfTargetSize);
            sBatch.Draw(texture, targetRect, sourceRect, color);
        }

        public static Rectangle GetRectangle(Vector2 position, Vector2 halfSize)
        {
            int x0 = (int)Math.Floor(position.X - halfSize.X);
            int y0 = (int)Math.Floor(position.Y - halfSize.Y);
            int x1 = (int)Math.Ceiling(position.X + halfSize.X);
            int y1 = (int)Math.Ceiling(position.Y + halfSize.Y);
            int width = x1 - x0;
            int height = y1 - y0;

            Rectangle rect = new Rectangle(x0, y0, width, height);

            return rect;
        }

        /**
         * return the point between start and end where the ratio of its distance
         * to start over that to end is equal to the ratio specified as argument
         */
        public static float LinearInterpolate(float start, float end, float ratio)
        {
            return start + (end - start) * ratio;
        }

        /**
         * compare 'source' as a whole or part of it against 'target'
         * in particular the comparison is between 'target' a substring of 'source' 
         * starting from the beginning with a length that equals that of 'target' 
         * if they are equal, the return value indicates the position of the character
         * that is next to the last character being compared in the source before processing 
         * otherwise it returns -1
         * if such a substring is not obtainable, the comparision method returns -1
         * the comparison is case-insensitive and all leading blank characters are
         * removed prior to the comparison
         */
        public static int CompareString(string source, string target)
        {
            string lowerredSource = source.ToLower();
            int i;
            for (i = 0; i < source.Length && char.IsWhiteSpace(lowerredSource[i]); i++)
            {
            }

            string processedSource = lowerredSource.Substring(i);
            int lenSource = processedSource.Length;
            int lenTarget = target.Length;
            if (lenSource < lenTarget)
            {
                return -1;
            }

            if (processedSource.Substring(0, lenTarget) != target)
            {
                return -1;
            }

            return i + lenTarget;
        }

        public static int GetAlphanumericWord(String s, out String result)
        {
            StringBuilder sb = new StringBuilder();

            // trimming
            int i;  /* number of leading spaces */
            for (i = 0; i < s.Length && char.IsWhiteSpace(s[i]); i++)
            {
            }

            s = s.Substring(i);

            foreach (char c in s)
            {
                if (char.IsLetterOrDigit(c))
                {
                    sb.Append(c);
                }
            }
            result = sb.ToString();

            return i + result.Length;
        }

        public static Matrix GetRotationMatrix(float radians)
        {
            return Matrix.CreateRotationZ(radians);
        }
    }
}
